package com.ficus.road.maintain.task;

import com.ficus.road.maintain.core.model.GpsHistory;
import com.ficus.road.maintain.core.model.TheoryGps;
import com.ficus.road.maintain.dao.GpsHistoryDao;
import com.ficus.road.maintain.service.TheoryGpsService;
import com.ficus.road.maintain.util.LngLonUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 修复gps跳点问题
 *
 * @author xiawen yang
 * @date 2022/4/7 下午5:01
 */
@Component
@Slf4j
public class GpsFixTask {

    @Autowired
    private TheoryGpsService theoryGpsService;
    @Autowired
    private GpsHistoryDao gpsHistoryDao;
    @Value("${coverage.jump_threshold}")
    private Double threshold;

    //    @Scheduled(cron = "0/5 * * * * ?")
//    @Scheduled(cron = "${coverage.fix_cron}")
    public void gpsFix() {

        // 测试代码
//        theoryGpsService.createTestGps();
        log.info("跳点修复任务开始...");
        long l = System.currentTimeMillis();
        List<GpsHistory> toBeFixed = gpsHistoryDao.findByQuery(new Query(Criteria.where("is_fixed").is(Boolean.FALSE)));
        Map<String, List<GpsHistory>> carIdGpsMap = toBeFixed.stream().sorted(Comparator.comparing(GpsHistory::getTimestamp))
                .collect(Collectors.groupingBy(GpsHistory::getCarId));
        carIdGpsMap.keySet().parallelStream().forEach(carId -> {
            List<GpsHistory> gpsList = carIdGpsMap.get(carId);
            GpsHistory lastGpsHistory = gpsList.get(gpsList.size() - 1);
            for (int i = 0; i < gpsList.size() - 1; i++) {
                GpsHistory pre = gpsList.get(i);
                GpsHistory post = gpsList.get(i + 1);
                //当前点位和最后一个点位相同，直接跳出循环
                if (LngLonUtil.calculateDistance(pre, lastGpsHistory).equals(0d)) {
                    log.info("跳点修复任务中断, 中断位置: timestamp: {}, carId: {}", pre.getTimestamp(), pre.getCarId());
                    break;
                }
                Double distance = LngLonUtil.calculateDistance(pre, post);
                // 出现跳点
                if (distance > threshold) {
                    TheoryGps preTheory = theoryGpsService.geoSearch(pre);
                    if (ObjectUtils.isEmpty(preTheory)) {
                        // pre点不在应覆盖路段
                        pre.setIsFixed(Boolean.TRUE);
                        gpsHistoryDao.saveOrUpdate(pre);
                        continue;
                    }
                    TheoryGps postTheory = theoryGpsService.geoSearch(post);
                    if (ObjectUtils.isEmpty(postTheory)) {
                        // post点不在应覆盖路段
                        post.setIsFixed(Boolean.TRUE);
                        gpsHistoryDao.saveOrUpdate(post);
                        continue;
                    }
                    if (!preTheory.getRoadInfoId().equals(postTheory.getRoadInfoId())) {
                        // 两个跳点对应的应覆盖点位不在同一路段
                        log.warn("两跳点处于不同路段, carId: {}. preRoadName: {}, postRoadName {}", carId, preTheory.getRoadName(), postTheory.getRoadName());
                        continue;
                    }
                    // 找到应补点
                    List<TheoryGps> theoryGpsList = theoryGpsService.querySameRoadTheoryGpsList(preTheory, postTheory);
                    log.info("开始修复跳点, roadId: {}, roadName: {}, 修复时间戳: {}", preTheory.getRoadInfoId(), preTheory.getRoadName(), pre.getTimestamp());
                    int fixWindow = theoryGpsList.size();
                    int start = i - fixWindow + 2;
                    if (start < 0) {
                        log.warn("gps轨迹异常, 异常carId: {}, 异常时间戳: {}", pre.getCarId(), pre.getTimestamp());
                    }
                    for (int j = i + 1; j >= start && j >= 0; j--) {
                        GpsHistory gps = gpsList.get(j);
                        gps.setLongitude(theoryGpsList.get(j - start).getLocation().get(0));
                        gps.setLatitude(theoryGpsList.get(j - start).getLocation().get(1));
                        gps.setIsFixed(Boolean.TRUE);
                        gpsHistoryDao.saveOrUpdate(gps);
                    }

//                    for (int j = start; j <= i; j++) {
//                        GpsHistory gps = gpsList.get(j);
//                        gps.setLongitude(theoryGpsList.get(j - start).getLocation().get(0));
//                        gps.setLatitude(theoryGpsList.get(j - start).getLocation().get(1));
//                        gps.setIsFixed(Boolean.TRUE);
//                        gpsHistoryDao.saveOrUpdate(gps);
//                    }
                } else {
                    pre.setIsFixed(Boolean.TRUE);
                    gpsHistoryDao.saveOrUpdate(pre);
                    post.setIsFixed(Boolean.TRUE);
                    gpsHistoryDao.saveOrUpdate(post);
                }
            }
        });
        log.info("跳点修复任务耗时: {}s", (System.currentTimeMillis() - l) / 1000);
    }

    /**
     * gps修复后处理
     */
    @Scheduled(cron = "${coverage.post_fix_cron}")
    public void postGpsFix() {
        log.info("跳点修复后处理开始...");
        List<GpsHistory> toBeFixed = gpsHistoryDao.findByQuery(new Query(Criteria.where("is_fixed").is(Boolean.FALSE)));
        Map<String, List<GpsHistory>> carIdGpsMap = toBeFixed.stream().sorted(Comparator.comparing(GpsHistory::getTimestamp))
                .collect(Collectors.groupingBy(item -> item.getCarId()));
        carIdGpsMap.keySet().parallelStream().forEach(carId -> {
            List<GpsHistory> gpsList = carIdGpsMap.get(carId);
            int size = gpsList.size();
            if (LngLonUtil.calculateDistance(gpsList.get(0), gpsList.get(size - 1)).equals(0d)) {
                gpsList.forEach(gps -> {
                    gps.setIsFixed(Boolean.TRUE);
                    gpsHistoryDao.saveOrUpdate(gps);
                });
                return;
            } else {
                log.error("跳点修复任务存在bug, 请联系管理员");
            }
        });
    }
}
